﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Burraco.Library
{
    /// <summary>
    /// Elenco di possibilità di incontro fra compagni durante il torneo
    /// </summary>
    public enum ModalitaCompagni { Mai, SiAvversarioNoCoppia, SiCoppiaNoAvversario, Sempre, CoppieFisse }

    /// <summary>
    /// Elenco di modalità di incontro tra partecipanti tenendo conto del sesso del giocatore
    /// </summary>
    public enum ModalitaSesso { TuttiControTutti, MaschiVsFemmine, MaschioConFemmina, SessoUnico }

    /// <summary>
    /// Classe che rappresenta il torneo
    /// </summary>
    public class Torneo
    {

        private List<Giocatore> giocatori;
        private List<Partita> partite;
        private List<Coppia> coppieUsate;
        private List<ITavolo> tavoliUsati;
        private List<Giocatore> giocatoriInTavoloDa2;
        private List<Giocatore> giocatoriInTavoloDa3;

        public int NumeroPartite { get; set; }
        public TimeSpan TempoCreazione { get; private set; }
        public int LimiteRipetiCoppia { get; set; }
        public int LimiteRipetiAvversario { get; set; }
        public int LimiteRipetiTavolo { get; set; }
        public int LimiteRipetiInTavoloZoppo { get; set; }
        public int LimiteRipetiInTavoloZoppoDa2 { get; set; }
        public int LimiteRipetiInTavoloZoppoDa3 { get; set; }
        public ModalitaCompagni RegolaCompagni { get; set; }
        public ModalitaSesso RegolaSesso { get; set; }


        private bool inCalcolo = false;

        public delegate void RicominciaEventHandler(Torneo sender, EventArgs e);
        public event RicominciaEventHandler onRicomincia;

        public delegate void PartitaCreataEventHandler(Torneo sender, PartitaCreataEventArgs e);
        public event PartitaCreataEventHandler onPartitaCreata;

        public delegate void TorneoGeneratoEventHandler(Torneo sender, EventArgs e);
        public event TorneoGeneratoEventHandler onTorneoGenerato;

        public delegate void TentativoFallitoEventHandler(Partita sender, TentativoFallitoEventArgs eventargs);
        public event TentativoFallitoEventHandler onTentativoFallito;
        public delegate void TavoloDa2NonValidoEventHandler(Partita sender, TavoloDa2NonValidoEventArgs eventargs);
        public event TavoloDa2NonValidoEventHandler onTavoloDa2NonValido;
        public delegate void TavoloDa3NonValidoEventHandler(Partita sender, TavoloDa3NonValidoEventArgs eventargs);
        public event TavoloDa3NonValidoEventHandler onTavoloDa3NonValido;
        public delegate void TavoloDa4NonValidoEventHandler(Partita sender, TavoloDa4NonValidoEventArgs eventargs);
        public event TavoloDa4NonValidoEventHandler onTavoloDa4NonValido;
        public delegate void TavoloDa4ValidoEventHandler(Partita sender, TavoloDa4ValidoEventArgs eventargs);
        public event TavoloDa4ValidoEventHandler onTavoloDa4Valido;
        public delegate void CoppiaValidaEventHandler(Partita sender, CoppiaValidaEventArgs eventargs);
        public event CoppiaValidaEventHandler onCoppiaValida;
        public delegate void CoppiaNonValidaEventHandler(Partita sender, CoppiaNonValidaEventArgs eventargs);
        public event CoppiaNonValidaEventHandler onCoppiaNonValida;
        public delegate void GeneraCoppieCompletatoEventHandler(Partita sender, GeneraCoppieCompletatoEventArgs eventargs);
        public event GeneraCoppieCompletatoEventHandler onGeneraCoppieCompletato;
        public delegate void GeneraTavoliCompletatoEventHandler(Partita sender, GeneraTavoliCompletatoEventArgs eventargs);
        public event GeneraTavoliCompletatoEventHandler onGeneraTavoliCompletato;

        private Random rnd = new Random();

        public Torneo()
        {
            partite = new List<Partita>();
            giocatori = new List<Giocatore>();
            giocatoriInTavoloDa2 = new List<Giocatore>();
            giocatoriInTavoloDa3 = new List<Giocatore>();
            coppieUsate = new List<Coppia>();
            tavoliUsati = new List<ITavolo>();
            
            RegolaCompagni = ModalitaCompagni.Mai;
            RegolaSesso = ModalitaSesso.TuttiControTutti;

            LimiteRipetiCoppia = 0;
            LimiteRipetiAvversario = 0;
            LimiteRipetiTavolo = 0;
            LimiteRipetiInTavoloZoppo = 0;
            LimiteRipetiInTavoloZoppoDa2 = 0;
            LimiteRipetiInTavoloZoppoDa3 = 0;
        
        }


        /// <summary>
        /// Crea una nuova partita e la aggiunge al torneo
        /// </summary>
        /// <returns>La nuova partita appena aggiunta</returns>
        public Partita NuovaPartita()
        {
            Partita p = new Partita(this, rnd);

            Partita.TentativoFallitoEventHandler TentativoFallitoEH = new Partita.TentativoFallitoEventHandler(delegate(Partita sender, TentativoFallitoEventArgs ea)
            {
                if (this.onTentativoFallito != null) this.onTentativoFallito(sender, ea);
            });
            p.onTentativoFallito += TentativoFallitoEH;

            Partita.TavoloDa2NonValidoEventHandler TavoloDa2NonValidoEH = new Partita.TavoloDa2NonValidoEventHandler(delegate(Partita sender, TavoloDa2NonValidoEventArgs ea)
            {
                if (this.onTavoloDa2NonValido != null) this.onTavoloDa2NonValido(sender, ea);
            });
            p.onTavoloDa2NonValido += TavoloDa2NonValidoEH;

            Partita.TavoloDa3NonValidoEventHandler TavoloDa3NonValidoEH = new Partita.TavoloDa3NonValidoEventHandler(delegate(Partita sender, TavoloDa3NonValidoEventArgs ea)
            {
                if (this.onTavoloDa3NonValido != null) this.onTavoloDa3NonValido(sender, ea);
            });
            p.onTavoloDa3NonValido += TavoloDa3NonValidoEH;

            Partita.TavoloDa4NonValidoEventHandler TavoloDa4NonValidoEH = new Partita.TavoloDa4NonValidoEventHandler(delegate(Partita sender, TavoloDa4NonValidoEventArgs ea)
            {
                if (this.onTavoloDa4NonValido != null) this.onTavoloDa4NonValido(sender, ea);
            });
            p.onTavoloDa4NonValido += TavoloDa4NonValidoEH;

            Partita.TavoloDa4ValidoEventHandler TavoloDa4ValidoEH = new Partita.TavoloDa4ValidoEventHandler(delegate(Partita sender, TavoloDa4ValidoEventArgs ea)
            {
                if (this.onTavoloDa4Valido != null) this.onTavoloDa4Valido(sender, ea);
            });
            p.onTavoloDa4Valido += TavoloDa4ValidoEH;

            Partita.CoppiaValidaEventHandler coppiaValidaEH = new Partita.CoppiaValidaEventHandler(delegate(Partita sender, CoppiaValidaEventArgs ea)
            {
                if (this.onCoppiaValida != null) this.onCoppiaValida(sender, ea);
            });
            p.onCoppiaValida += coppiaValidaEH;

            Partita.CoppiaNonValidaEventHandler coppiaNonValidaEH = new Partita.CoppiaNonValidaEventHandler(delegate(Partita sender, CoppiaNonValidaEventArgs ea)
            {
                if (this.onCoppiaNonValida != null) this.onCoppiaNonValida(sender, ea);
            });
            p.onCoppiaNonValida += coppiaNonValidaEH;

            Partita.GeneraCoppieCompletatoEventHandler GeneraCoppieCompletatoEH = new Partita.GeneraCoppieCompletatoEventHandler(delegate(Partita sender, GeneraCoppieCompletatoEventArgs ea)
            {
                if (this.onGeneraCoppieCompletato != null) this.onGeneraCoppieCompletato(sender, ea);
            });
            p.onGeneraCoppieCompletato += GeneraCoppieCompletatoEH;

            Partita.GeneraTavoliCompletatoEventHandler GeneraTavoliCompletatoEH = new Partita.GeneraTavoliCompletatoEventHandler(delegate(Partita sender, GeneraTavoliCompletatoEventArgs ea)
            {
                if (this.onGeneraTavoliCompletato != null) this.onGeneraTavoliCompletato(sender, ea);
            });
            p.onGeneraTavoliCompletato += GeneraTavoliCompletatoEH;


            partite.Add(p);
            return p;
        }

        public List<Partita> getListaPartite()
        {
            return partite;
        }
        public List<Coppia> getListaCoppieUsate()
        {
            return coppieUsate;
        }
        public List<ITavolo> getListaTavoliUsati()
        {
            return tavoliUsati;
        }

        public List<Giocatore> getListaGiocatoriInTavoloDa2()
        {
            return giocatoriInTavoloDa2;
        }
        public List<Giocatore> getListaGiocatoriInTavoloDa3()
        {
            return giocatoriInTavoloDa3;
        }
        public List<Giocatore> getListaGiocatori()
        {
            return giocatori;
        }
        public void setListaGiocatori(List<Giocatore> lista)
        {
            giocatori = lista;
        }

        //public void stampaListaGiocatori()
        //{
			
        //    int k = giocatori.size() % 4;
        //    if (k == 2) Logger.println("il resto è : " + k +"\n");	// controllo per tavolo zoppo
        //    else Logger.println("non c'è resto \n");
				
        //    for (int i=0; i < giocatori.size(); i++) {
        //        Giocatore gc = giocatori.get(i).getCompagno();
				
        //        Logger.print("Giocatore " + (i + 1) + " : \t" + gc.NomeCompleto);
        //        Logger.print(
        //                gc.getSesso() == SessoGiocatore.SessoMaschile ? "\tè il compagno di\t " : "\tè la compagna di\t "
        //            );

        //        Logger.print(giocatori.get(i).NomeCompleto);
        //        Logger.println();			
        //    }
			
        //    Logger.println();		
        //}

        public int CalcolaNumeroPartite()
        {
            int nPartite = 0;

            int nGiocatori = giocatori.Count;
            int parity = 4;
            if (RegolaSesso == ModalitaSesso.TuttiControTutti)
            {
                if (RegolaCompagni == ModalitaCompagni.Sempre)
                {
                    nPartite = (nGiocatori - 1) / 3;
                    nPartite = ((nGiocatori / parity) * parity) - 1;
                }
                else if (RegolaCompagni == ModalitaCompagni.SiAvversarioNoCoppia || RegolaCompagni == ModalitaCompagni.SiCoppiaNoAvversario)
                {
                    nPartite = (nGiocatori - 2) / 3;
                }
                else if (RegolaCompagni == ModalitaCompagni.Mai)
                {
                    nPartite = (nGiocatori - 2) / 3; ;
                }
            }
            else
            {
                if (RegolaCompagni == ModalitaCompagni.Sempre)
                {
                    nPartite = ((nGiocatori / parity) * parity) / 2;
                }
                else if (RegolaCompagni == ModalitaCompagni.Mai || RegolaCompagni == ModalitaCompagni.SiAvversarioNoCoppia)
                {
                    nPartite = (((nGiocatori / parity) * parity) / 2) - 1;
                }
            }

            return nPartite;
        }

        public void Ricomincia()
        {
            coppieUsate.Clear();
            tavoliUsati.Clear();
            partite.Clear();
            giocatoriInTavoloDa2.Clear();
            giocatoriInTavoloDa3.Clear();

            foreach (Giocatore g in giocatori)
            {
                g.ClearListaAvversari();
                g.ClearListaCompagni();
            }

            if (inCalcolo && onRicomincia != null) onRicomincia(this, new EventArgs());
        }

        public void AggiungiGiocatore(Giocatore g)
        {
            giocatori.Add(g);
            g.Torneo = this;
        }

        public void creaGiocatoriTest(int nGiocatori)
        {
            for (int i = 0; i < nGiocatori; i += 2)
            {
                Giocatore g1, g2;
                g1 = new Giocatore("  " + (i + 1 >= 10 ? (i + 1).ToString() : (" " + (i + 1))) + "  ", "", SessoGiocatore.SessoMaschile);
                if (nGiocatori >= (i + 2))
                {
                    g2 = new Giocatore("  " + (i + 2 >= 10 ? (i + 2).ToString() : " " + (i + 2)) + "  ", "", SessoGiocatore.SessoFemminile);
                    g2.Compagno = g1;
                    AggiungiGiocatore(g2);
                    g1.Compagno = g2;
                }
                AggiungiGiocatore(g1);

            }
        }

        public void GeneraTorneo() {
            DateTime startTime = DateTime.Now;
            inCalcolo = true;
            while (partite.Count < NumeroPartite)
            {

                Partita p = NuovaPartita();

                p.generaTavoli();

                if (onPartitaCreata != null) onPartitaCreata(this, new PartitaCreataEventArgs(p, partite.Count));
            }
            inCalcolo = false;
            DateTime stopTime = DateTime.Now;
            TempoCreazione = stopTime - startTime;

            if (onTorneoGenerato != null) onTorneoGenerato(this, new EventArgs());
        }
    }

    public class PartitaCreataEventArgs : EventArgs
    {
        public Partita PartitaCreata { get; set; }
        public int NumeroPartiteCreate { get; set; }
        public PartitaCreataEventArgs(Partita p, int num)
        {
            PartitaCreata = p;
            NumeroPartiteCreate = num;
        }
    }
}
